﻿using System;
using System.Globalization;
using System.Collections.Generic;

using SBPweb.Forge.Contracts.Extensibility;

namespace SBPweb.Forge.Contracts.Extensibility
{

    public abstract class ExtendableBase : IExtendable
    {

        #region Private fields

        protected Dictionary<string, object> propertyValues = new Dictionary<string, object>();
        protected Dictionary<string, Type> propertyTypes = new Dictionary<string, Type>();

        #endregion

        #region Events

        public event EventHandler<PropertyChangedEventArgs> PropertyChanged;

        #endregion

        #region Property accessors

        public object this[string propertyName]
        {
            get
            {
                return Get<object>(propertyName);
            }
            set
            {
                Put<object>(propertyName, value);
            }
        }

        public object this[int index]
        {
            get
            {
                return Get<object>(index);
            }
            set
            {
                Put<object>(index, value);
            }
        }

        #endregion

        #region Methods

        public virtual bool Contains(string propertyName)
        {
            lock (this)
            {
                return propertyValues.ContainsKey(propertyName);
            }
        }

        public virtual T Get<T>(string propertyName)
        {
            lock (this)
            {
                if (!propertyTypes.ContainsKey(propertyName))
                {
                    return default(T);
                }
                
                object ret = propertyValues[propertyName];
                Type t = typeof(T);
                if ((propertyTypes.ContainsValue(t)) //Data type contained
                    ||(t.IsGenericType && t.GetGenericTypeDefinition().Equals(typeof(Nullable<>))) //Nullable value
                    || t == typeof(object)) //T is object
                {
                    return (T)ret;
                }

                return (T)Convert.ChangeType(ret, t, CultureInfo.CurrentUICulture);
            }
        }

        public virtual T Get<T>(int index)
        {
            return Get<T>(GetNameByIndex(index));
        }

        public virtual void Put<T>(string propertyName, T value)
        {
            lock (this)
            {
                Type t = typeof(T);
                if (propertyValues.ContainsKey(propertyName))
                {
                    propertyValues[propertyName] = value;
                    propertyTypes[propertyName] = t;
                }
                else
                {
                    propertyValues.Add(propertyName, value);
                    propertyTypes.Add(propertyName, t);
                }
                OnPropertyChanged(propertyName);
            }
        }

        public virtual void Put<T>(int index, T value)
        {
            Put<T>(GetNameByIndex(index), value);
        }

        public virtual void Remove(string propertyName)
        {
            Remove<object>(propertyName);
        }

        public virtual void Remove<T>(string propertyName)
        {
            lock (this)
            {
                if (propertyValues.ContainsKey(propertyName))
                {
                    propertyValues.Remove(propertyName);
                }
                if (propertyTypes.ContainsKey(propertyName))
                {
                    propertyTypes.Remove(propertyName);
                }
                OnPropertyChanged(propertyName);
            }
        }

        public virtual void Remove(int index)
        {
            Remove<object>(index);
        }

        public virtual void Remove<T>(int index)
        {
            Remove<T>(GetNameByIndex(index));
        }

        protected void OnPropertyChanged(string propertyName)
        {
            PropertyChangedEventArgs e = new PropertyChangedEventArgs(propertyName);
            OnPropertyChanged(this, e);
        }

        protected virtual void OnPropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            if (PropertyChanged != null)
            {
                PropertyChanged(this, e);
            }
        }

        private string GetNameByIndex(int index)
        {
            lock (this)
            {
                int i = 0;
                foreach (KeyValuePair<string, object> kvp in propertyValues)
                {
                    if (i++ == index)
                    {
                        return kvp.Key;
                    }
                }
                return null;
            }
        }

        //private int GetIndexByName(string name)
        //{
        //}

        #endregion

    }

}